﻿using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using SDCloudSolution.SDCloud.DataLayer.Model.Interfaces;
using SDCloudSolution.SDCloud.Entities;
using SDCloudSolution.SDCloud.Entities.Models;

namespace SDCloudSolution.SDCloud.DataLayer.Model
{
    public class ModelRunDataProvider : IModelRunDataProvider
    {
        private readonly ModelDbContext _modelDbContext;

        public ModelRunDataProvider(ModelDbContext modelDbContext)
        {
            _modelDbContext = modelDbContext;
        }

        protected IQueryable<ModelRunEntity> ModelRuns
        {
            get { return _modelDbContext.ModelRuns.AsNoTracking(); }
        }

        /// <summary>
        /// Gets a specific model run information by specific identifier.
        /// </summary>
        /// <param name="modelRunId">The model run identifier.</param>
        public Task<ModelRunEntity> GetByIdAsync(int modelRunId)
        {
            return ModelRuns.FirstOrDefaultAsync(model => model.Id == modelRunId);
        }

        /// <summary>
        /// Gets a collection with paging of model runs for specific model identifier.
        /// </summary>
        /// <param name="modelId">The model identifier.</param>
        /// <param name="paging">The paging.</param>
        public Task<EntityCollection<ModelRunEntity>> GetByModelIdAsync(int modelId, Paging paging)
        {
            return ModelRuns
                .Where(model => model.ModelId == modelId)
                .ToEntityCollectionAsync(paging);
        }

        public Task<EntityCollection<ModelRunEntity>> FindByModelIdsAsync(IEnumerable<int> modelIds, Paging paging)
        {
            var _modelIds = modelIds.ToList();

            return ModelRuns
                .Where(model => _modelIds.Contains(model.ModelId))
                .OrderByDescending(model => model.QueuedDateUtc)
                .ToEntityCollectionAsync(paging);
        }

        public Task<EntityCollection<ModelRunEntity>> FindByModelIdsAndStatusesAsync(IEnumerable<int> modelIds, IEnumerable<ModelRunStatus> statuses, Paging paging)
        {
            var _modelIds = modelIds.ToList();
            var _statuses = statuses.ToList();

            return ModelRuns
                .Where(model => _modelIds.Contains(model.ModelId) && _statuses.Contains(model.Status))
                .OrderByDescending(model => model.QueuedDateUtc)
                .ToEntityCollectionAsync(paging);
        }

        /// <summary>
        /// Finds a collection with paging of model runs for specific model identifier and filtered by specific status.
        /// </summary>
        /// <param name="modelId">The model identifier.</param>
        /// <param name="status">The status.</param>
        /// <param name="paging">The paging.</param>
        public Task<EntityCollection<ModelRunEntity>> FindByModelIdAndStatusAsync(int modelId, ModelRunStatus status, Paging paging)
        {
            return ModelRuns
                .Where(model => model.ModelId == modelId && model.Status == status)
                .ToEntityCollectionAsync(paging);
        }

        public Task<ModelRunEntity> FindLastModelRunAsync(int modelId)
        {
            return ModelRuns
                .OrderByDescending(model => model.QueuedDateUtc)
                .FirstOrDefaultAsync(model => model.ModelId == modelId);
        }

        /// <summary>
        /// Enqueue a few model runs from queue.
        /// </summary>
        /// <param name="count">The count.</param>
        public Task<ModelRunEntity> EnqueueAsync(int count)
        {
            return ModelRuns
                .Where(model => model.Status == ModelRunStatus.Queued)
                .OrderBy(model => model.Priority)
                .Take(count)
                .FirstOrDefaultAsync();
        }

        /// <summary>
        /// Inserts a new instance of model run.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public Task InsertAsync(ModelRunEntity entity)
        {
            _modelDbContext.ModelRuns.Add(entity);
            return _modelDbContext.SaveChangesAsync();
        }

        /// <summary>
        /// Updates an exists instance of model run.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public Task UpdateAsync(ModelRunEntity entity)
        {
            // Attach and force update of this entity
            _modelDbContext.ModelRuns.Attach(entity);
            _modelDbContext.Entry(entity).State = EntityState.Modified;
            return _modelDbContext.SaveChangesAsync();
        }
    }
}
